#include <drivers/uart.h>
#include <kernel/initcall.h>
#include <stm32f10x.h>
#include <drivers/irq.h>
#include <drivers/gpio.h>


#define  STM32F10X_UART_SR(uart_base)                *(volatile uint32_t *)(uart_base + 0)
#define  STM32F10X_UART_DR(uart_base)                *(volatile uint32_t *)(uart_base + 0x4)
#define  STM32F10X_UART_BRR(uart_base)               *(volatile uint32_t *)(uart_base + 0x8)
#define  STM32F10X_UART_CR1(uart_base)               *(volatile uint32_t *)(uart_base + 0xC)
#define  STM32F10X_UART_CR2(uart_base)               *(volatile uint32_t *)(uart_base + 0x10)
#define  STM32F10X_UART_CR3(uart_base)               *(volatile uint32_t *)(uart_base + 0x14)
#define  STM32F10X_UART_GTPR(uart_base)              *(volatile uint32_t *)(uart_base + 0x18)


void stm32f10x_uart_init(struct uart_device *device)
{
    volatile uint32_t uart_base = (volatile uint32_t)device->io_base;
    STM32F10X_UART_CR1(uart_base) |= (1<<13);
    STM32F10X_UART_CR1(uart_base) |= (1<<2);
    STM32F10X_UART_CR1(uart_base) |= (1<<5);
    STM32F10X_UART_CR1(uart_base) |= (1<<3);

    uint32_t integer = 4500000 / device->baudrate;
    //uint32_t decimal = 1; //maybe it's better?
    uint32_t decimal = ((4500000 - device->baudrate * integer)*16 / device->baudrate);
    STM32F10X_UART_BRR(uart_base) = (integer<<4) | (decimal);
}


static void stm32f10x_uart_start(struct uart_device *device)
{

}


static void stm32f10x_uart_stop(struct uart_device *device)
{

}


static void stm32f10x_uart_put_char(struct uart_device *device, char ch)
{
    STM32F10X_UART_DR(device->io_base) = ch;
    while(!(STM32F10X_UART_SR(device->io_base) & (1<<6)));
}


static void stm32f10x_uart_put_string(struct uart_device *device, char *str)
{
    while(*str) {
	    STM32F10X_UART_DR(device->io_base) = *str;
        while(!(STM32F10X_UART_SR(device->io_base) & (1<<6)));
        str++;
    }
}


static char stm32f10x_uart_get_char(struct uart_device *device)
{
    while((STM32F10X_UART_SR(device->io_base) & (1<<5))==0x20)
	    return STM32F10X_UART_DR(device->io_base);
}


static void stm32f10x_uart_set_baudrate(struct uart_device *device, uint32_t baudrate)
{

}


struct uart_ops stm32f10x_uart_ops = {
    .start = stm32f10x_uart_start,
    .stop = stm32f10x_uart_stop,
    .put_char = stm32f10x_uart_put_char,
    .put_string = stm32f10x_uart_put_string,
    .get_char = stm32f10x_uart_get_char,
    .set_baudrate = stm32f10x_uart_set_baudrate,
};


static int stm32f10x_uart_probe(struct uart_device *dev)
{
    irq_request(dev->irq_num, dev->callback);
    stm32f10x_uart_init(dev);

    return 0;
}


static void stm32f10x_uart_remove(struct uart_device *dev)
{

}


struct uart_driver stm32f10x_uart = {
    .compatible = "stm32f10x-uart",
    .probe = stm32f10x_uart_probe,
    .remove = stm32f10x_uart_remove,
    .ops = &stm32f10x_uart_ops,
};


static int stm32f10x_uart_driver_register(void)
{
    return uart_driver_register(&stm32f10x_uart);
}


base_initcall(stm32f10x_uart_driver_register, 1);
